// Network.cpp: implementation of the FCNetwork class.
//
//////////////////////////////////////////////////////////////////////

#include "FCFarmari/Network.h"
#include "FCFarmari/Cell.h"
#include <math.h>
#include "stdio.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

FCNetwork::FCNetwork(int InputNumb, int HidenNum, double alfa_, double delta_)
{

	LayersNum = 2;
	Alfa = alfa_;
	Delta = delta_;
	NumInLayer = new int[LayersNum+2][2];
	NumInLayer[0][0] = 0;  //vstupy do NS
	NumInLayer[0][1] = InputNumb;  //reprezentuje pocet vstupov do siete
	NumInLayer[1][0] = NumInLayer[0][0] + NumInLayer[0][1];//9;
	NumInLayer[1][1] = HidenNum;
	NumInLayer[2][0] = NumInLayer[1][0] + NumInLayer[1][1];//19;		
	NumInLayer[2][1] = 1;  //reprezentuje pocet vystupov zo siete
	NumInLayer[3][1] = 0;
	


	NeuronNum = NumInLayer[1][1] + NumInLayer[2][1] + NumInLayer[0][1]; //vstupna vrstva included
	Neurons = new FCCell* [NeuronNum];
}

FCNetwork::FCNetwork(const FCNetwork& network)
{
	LayersNum = network.LayersNum;
	Alfa = network.Alfa;
	Delta = network.Delta;
	NumInLayer = new int[LayersNum+2][2];
	NumInLayer[0][0] = 0;  //vstupy do NS
	NumInLayer[0][1] = network.NumInLayer[0][1];  //reprezentuje pocet vstupov do siete
	NumInLayer[1][0] = network.NumInLayer[1][0];
	NumInLayer[1][1] = network.NumInLayer[1][1];
	NumInLayer[2][0] = network.NumInLayer[2][0]; 
	NumInLayer[2][1] = 1;  //reprezentuje pocet vystupov zo siete
	NumInLayer[3][1] = 0;
	
	NeuronNum = network.NeuronNum;
	Neurons = new FCCell* [NeuronNum];
	this->CopyWeights(&network);
	
}

FCNetwork::FCNetwork()
{

}

FCNetwork::~FCNetwork()
{
	delete [] NumInLayer;
	for(int i = 0; i < NeuronNum; ++i)
		delete Neurons[i];
	delete [] Neurons;
}

void FCNetwork::Inicialize()
{
	
	FCCell * newCell;
	int index = NumInLayer[1][0];
	
  	for(int i = 1; i <= LayersNum ; ++i)
		for(int j = 0; j < NumInLayer[i][1] ; ++j)
		{
			newCell = new FCCell;
			newCell->Layer = i;
			newCell->Weights = new double[NumInLayer[i-1][1]];
			newCell->WeightsDelta = new double[NumInLayer[i-1][1]]; 
			newCell->WeightsNum = NumInLayer[i-1][1];
			newCell->InitWeights();
			Neurons[index] = newCell;
			index++;
		}
		
	for(int j = 0; j < NumInLayer[0][1] ; ++j)   //inicializacia vstupov
	{
		newCell = new FCCell;
		newCell->Layer = 0;
		//newCell->fNet = 0;
		//newCell->Net = 0;
		Neurons[j] = newCell;
	}
}


void FCNetwork::CopyWeights(const FCNetwork *network)
{
	FCCell * newCell;
	int index = NumInLayer[1][0];
	
  	for(int i = 1; i <= LayersNum ; ++i)
		for(int j = 0; j < NumInLayer[i][1] ; ++j)
		{
			newCell = new FCCell(*network->Neurons[index]);
			Neurons[index] = newCell;
			index++;
		}
		
	for(int j = 0; j < NumInLayer[0][1] ; ++j)   //inicializacia vstupov
	{
		newCell = new FCCell;
		newCell->Layer = 0;
		//newCell->fNet = 0;
		//newCell->Net = 0;
		Neurons[j] = newCell;
	}
	
}


double FCNetwork::GetOutput(int pull)
{

	int CurrentLayer = 1;
	int PrevLayerItem = 0;
	
	for(int CurrentNeuron = NumInLayer[1][0]; CurrentNeuron < NeuronNum; ++CurrentNeuron)
	{
		CurrentLayer = Neurons[CurrentNeuron]->Layer;
		Neurons[CurrentNeuron]->Net[pull] = 0;
		for(int j = 0;j < NumInLayer[CurrentLayer-1][1]; ++j) //prechod cez vsetky vystupy predch. vrstvy
		{
			PrevLayerItem = NumInLayer[CurrentLayer-1][0] + j;
			Neurons[CurrentNeuron]->Net[pull] += Neurons[PrevLayerItem]->fNet[pull]*Neurons[CurrentNeuron]->Weights[j];
		}

		Neurons[CurrentNeuron]->fNet[pull] =  ActivateFunc(Neurons[CurrentNeuron]->Net[pull]);
	}

	return Neurons[NeuronNum-1]->fNet[pull];
}

double FCNetwork::ActivateFunc(double Net)
{
	double Alfa = 0.2;

	return(1.0/(1+exp((-1)*Alfa*Net)));
}

void FCNetwork::BackProp(double Demand, int pull)
{
	
	Neurons[NumInLayer[2][0]]->fNet[pull] = Demand;

	Epsilon(pull, NumInLayer[2][0], Neurons[NumInLayer[2][0]]->WeightsNum, true);

  int CurrentNeuron;

	for(CurrentNeuron = NeuronNum-2; CurrentNeuron >= NumInLayer[1][0]; --CurrentNeuron)
	{

		Epsilon(pull, CurrentNeuron, Neurons[CurrentNeuron]->WeightsNum, false);

	}

	for(CurrentNeuron = NeuronNum-1; CurrentNeuron >= NumInLayer[1][0]; --CurrentNeuron)
	{
		for(int j = 0;j < Neurons[CurrentNeuron]->WeightsNum; ++j)	//uprava vahy
		{
			Neurons[CurrentNeuron]->Weights[j] += Neurons[CurrentNeuron]->WeightsDelta[j];
		}
	}

}

double FCNetwork::TrainNN(double *array, int ArrayNum, double demand)
{

	return 0;//(0.5*pow(demand - GetOutput(),2));
}

void FCNetwork::SetInput(double *array, int pull)
{
	for(int j = 0; j < NumInLayer[0][1] ; ++j)   //inicializacia vstupov
	{
		Neurons[j]->Net[pull] = array[j];
		Neurons[j]->fNet[pull] = array[j];
	}
}



void FCNetwork::Epsilon(int pull, int CurrentNeuron, int WeightNum, bool Top)
{

	int WeIndex = 0;
	double Temp;
	double E1;
	int top = NumInLayer[2][0];
	double WeightDif = 0.0;
	int PrevLayerItem;

	if(Top)
	{
		for(int IndexW = 0; IndexW < WeightNum; ++ IndexW)
		{
			WeightDif = 0.0;
			E1 = Neurons[top]->fNet[0]*(1 - Neurons[top]->fNet[0])*Neurons[NumInLayer[1][0]+IndexW]->fNet[0];
			//double WeightDif = (Neuron[output]->fNet[1] - Neuron[output]->fNet[0]*E1;
			for(int j = 0; j < pull; ++j) 
			{
				WeightDif +=Alfa*(Neurons[top]->fNet[j + 1] - Neurons[top]->fNet[j])*E1;
				E1 = Delta*E1 + Neurons[top]->fNet[j+1]*(1 - Neurons[top]->fNet[j+1])*Neurons[NumInLayer[1][0]+IndexW]->fNet[j+1];
			}

			Neurons[top]->WeightsDelta[IndexW] = WeightDif; 
		}
		
	}
	else
	{
		WeIndex = CurrentNeuron - NumInLayer[1][0];
		for(int IndexW = 0; IndexW < WeightNum; ++ IndexW)
		{
			WeightDif = 0.0;
			PrevLayerItem = NumInLayer[0][0] + IndexW;
			
			double E1 = Neurons[top]->fNet[0]*(1 - Neurons[top]->fNet[0])*Neurons[top]->Weights[WeIndex];
			E1 *= Neurons[CurrentNeuron]->fNet[0]*(1 - Neurons[CurrentNeuron]->fNet[0])*Neurons[PrevLayerItem]->fNet[0];
			//double WeightDif = (Neuron[output]->fNet[1] - Neuron[output]->fNet[0]*E1;
			for(int j = 0; j < pull; ++j) 
			{
				WeightDif +=Alfa*(Neurons[top]->fNet[j + 1] - Neurons[top]->fNet[j])*E1;
				E1 = Delta*E1;
				Temp = Neurons[top]->fNet[j+1]*(1 - Neurons[top]->fNet[j+1])*Neurons[top]->Weights[WeIndex];
				Temp *= Neurons[CurrentNeuron]->fNet[j+1]*(1 - Neurons[CurrentNeuron]->fNet[j+1])*Neurons[PrevLayerItem]->fNet[j+1];
				E1 += Temp;
			}
			Neurons[CurrentNeuron]->WeightsDelta[IndexW] = WeightDif;
		}	
	}
}

void FCNetwork::SaveAs(char name[])
{
	FILE *SaveFile;
		
	SaveFile = fopen(name,"w");

	for(int neuron = NumInLayer[1][0]; neuron < NeuronNum ; neuron++)
		for(int weight = 0; weight < Neurons[neuron]->WeightsNum ; weight++)
			fprintf(SaveFile,"%lf \n",Neurons[neuron]->Weights[weight]);
	fclose(SaveFile);
}

void FCNetwork::LoadFrom(char name[])
{
	FILE *SaveFile = NULL;
		
	SaveFile = fopen(name,"r");
	
	if (SaveFile == NULL)
		return;

	for(int neuron = NumInLayer[1][0]; neuron < NeuronNum ; neuron++)
		for(int weight = 0; weight < Neurons[neuron]->WeightsNum ; weight++)
			fscanf(SaveFile,"%lf \n",&Neurons[neuron]->Weights[weight]);
	printf("Vahy laded!\n");
	fclose(SaveFile);
}
